package org.luosl.webmagicx.conf

import play.api.libs.json._
import play.api.libs.functional.syntax._

/**
  * 爬虫配置信息
  *
  * @param startUrlCreators 起始url
  * @param targetUrlRegexs 目标url正则
  * @param components 组件
  * @param fields 字段
  * @param site 站点信息
  * @param attribute 爬虫属性
  */
case class SpiderConf(id:String,
                       desc:String,
                       startUrlCreators:Seq[Component],
                       targetUrlRegexs: Seq[TargetUrlRegex],
                       task:Option[Task],
                       proxies:Seq[Proxy],
                       components: Components,
                       fields:Seq[Field],
                       site: Site,
                       attribute: Attr,
                       xmlStr:String,
                       enabled: Boolean){
  val taskType:String = task.map(t=>s"corn;${t.corn}").getOrElse("once")
}

/**
  * 目标正则描述
  * @param extract 抽取表达式
  * @param priority 优先级
  * @param regex 正则
  */
case class TargetUrlRegex(extract: Option[Extract], priority:Long, regex:String)

/**
  * 定义抽取规则
  * @param types 抽取规则类型（xpath,cssSelector,jsonPath）
  * @param expression 抽取规则表达式
  */
case class Extract(types:String,expression:String)
/**
  * 爬虫字段配置
  * @param name 字段名称
  * @param extracts 抽取规则
  * @param must 是否必须
  */
case class Field(name:String, extracts:Seq[Extract], scope:String, textFormat:Boolean, must:Boolean)

/**
  * 站点信息配置
  * @param userAgent 浏览器标识
  * @param headers 请求头
  * @param cookies cookie
  */
case class Site(userAgent:String,headers:Map[String,String],cookies:Map[String,String])

/**
  * 组件
  * @param clazz clazz
  * @param  prop 组件属性
  */
case class Component(clazz:String,prop:XmlProps)
/**
  * 组件集合
  * @param pipelines 持久化管道
  */
case class Components(handlers:Seq[Component],pipelines:Seq[Component], scheduler: Option[Component])

/**
  *
  * @param maxDeep 最大深度
  * @param charset 字符集
  * @param timeout 超时
  * @param threadNum 线程数
  * @param retryTimes 重试次数
  */
case class Attr(maxDeep:Int, charset:String, timeout:Int, threadNum:Int, retryTimes:Int, sleep:Int)

/**
  * 任务
  * @param corn corn
  */
case class Task(startNow:Boolean, corn:String)

/**
  * 代理
  * @param host host
  * @param port port
  * @param user user
  * @param password password
  */
case class Proxy(host:String, port:Int, user:Option[String], password:Option[String])

object JsonWriter{

  implicit val componentWrites:Writes[Component] = new Writes[Component] {

    def writes(comp: Component):JsObject = Json.obj(
      "clazz" -> comp.clazz,
      "props" -> parseAny(comp.prop.dataList)
    )

    def parseAny(any: Any): JsObject ={
      any match {
        case ls:List[Any] => ls.foldLeft(Json.obj())((js,item) => js ++ parseAny(item))
        case (v1:String, v2:String) => Json.obj(v1 -> v2)
        case (v1:String, v2:List[(String, Any) @unchecked]) =>
          Json.obj(v1 -> v2.foldLeft(Json.obj()){(js, item)=>
            val key:String = item._1
            val value = item._2
            if(js.keys.contains(key)){
              js.value(key) match {
                case arr:JsArray => js ++ Json.obj(key -> arr.+:(parseAny(value)))
                case jsValue:JsValue => js ++ Json.obj(key -> Seq(jsValue, parseAny(value)))
              }
            }else{
              js ++ parseAny(item)
            }
          })
        case str:String => JsString(str).as[JsObject]
        case int:Int => JsNumber(int).as[JsObject]
        case double:Double => JsNumber(double).as[JsObject]
        case long:Long => JsNumber(long).as[JsObject]
      }
    }

  }

  implicit val scWrites:Writes[SpiderConf] = (sc:SpiderConf) => {
    Json.obj(
      "task" -> sc.task.map(t=> Json.obj("startNow" -> t.startNow, "corn" -> t.corn) ),
      "desc" -> sc.desc,
      "attribute" -> Json.obj(
        "threadNum" -> sc.attribute.threadNum,
        "charset" -> sc.attribute.charset,
        "maxDeep" -> sc.attribute.maxDeep,
        "retryTimes" -> sc.attribute.retryTimes,
        "sleep" -> sc.attribute.sleep,
        "timeout" -> sc.attribute.timeout
      ),
      "site" -> Json.obj(
        "cookies" -> sc.site.cookies,
        "headers" -> sc.site.headers
      ),
      "startUrlCreators" -> Json.arr(sc.startUrlCreators),
      "components" -> Json.obj(
        "scheduler" -> sc.components.scheduler,
        "handlers" -> sc.components.handlers,
        "pipelines" -> sc.components.pipelines
      ),
      "fields" -> sc.fields.map( f=> Json.obj(
        "name" -> f.name,
        "must" -> f.must,
        "scope" -> f.scope,
        "textFormat" -> f.textFormat,
        "extracts" -> f.extracts.map( e=> Json.obj(
          "type" -> e.types,
          "expression" -> e.expression
        ))
      )),
      "proxies" -> sc.proxies.map(p=> Json.obj(
        "host" -> p.host,
        "port" -> p.port,
        "user" -> p.user,
        "password" -> p.password
      ))
    )
  }
}
